SPORE.$Import('core.array');
SPORE.$Import('core.string');
SPORE.$Import('core.number');
SPORE.$Import('core.function');

/*
---

name: Browser

description: The Browser Object. Contains Browser initialization, Window and Document, and the Browser Hash.

license: MIT-style license.

requires: [Array, Function, Number, String]

provides: [Browser, Window, Document]

...
*/

SPORE.register('core.browser',function($ns){

	var document = this.document;
	var window = document.window = this;
	
	var UID = 1;
	
	this.$uid = (window.ActiveXObject) ? function(item){
		return (item.uid || (item.uid = [UID++]))[0];
	} : function(item){
		return item.uid || (item.uid = UID++);
	};
	
	$uid(window);
	$uid(document);
	
	var ua = navigator.userAgent.toLowerCase(),
		platform = navigator.platform.toLowerCase(),
		UA = ua.match(/(opera|ie|firefox|chrome|version)[\s\/:]([\w\d\.]+)?.*?(safari|version[\s\/:]([\w\d\.]+)|$)/) || [null, 'unknown', 0],
		mode = UA[1] == 'ie' && document.documentMode;
	
	var Browser = this.Browser = {
	
		extend: Function.prototype.extend,
	
		name: (UA[1] == 'version') ? UA[3] : UA[1],
	
		version: mode || parseFloat((UA[1] == 'opera' && UA[4]) ? UA[4] : UA[2]),
	
		Platform: {
			name: ua.match(/ip(?:ad|od|hone)/) ? 'ios' : (ua.match(/(?:webos|android)/) || platform.match(/mac|win|linux/) || ['other'])[0]
		},
	
		Features: {
			xpath: !!(document.evaluate),
			air: !!(window.runtime),
			query: !!(document.querySelector),
			json: !!(window.JSON)
		},
	
		Plugins: {}
	
	};
	
	Browser[Browser.name] = true;
	Browser[Browser.name + parseInt(Browser.version, 10)] = true;
	Browser.Platform[Browser.Platform.name] = true;
	
	// Request
	
	Browser.Request = (function(){
	
		var XMLHTTP = function(){
			return new XMLHttpRequest();
		};
	
		var MSXML2 = function(){
			return new ActiveXObject('MSXML2.XMLHTTP');
		};
	
		var MSXML = function(){
			return new ActiveXObject('Microsoft.XMLHTTP');
		};
	
		return Function.attempt(function(){
			XMLHTTP();
			return XMLHTTP;
		}, function(){
			MSXML2();
			return MSXML2;
		}, function(){
			MSXML();
			return MSXML;
		});
	
	})();
	
	Browser.Features.xhr = !!(Browser.Request);
	
	// Flash detection
	
	var version = (Function.attempt(function(){
		return navigator.plugins['Shockwave Flash'].description;
	}, function(){
		return new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
	}) || '0 r0').match(/\d+/g);
	
	Browser.Plugins.Flash = {
		version: Number(version[0] || '0.' + version[1]) || 0,
		build: Number(version[2]) || 0
	};
	
	// String scripts
	
	Browser.exec = function(text){
		if (!text) return text;
		if (window.execScript){
			window.execScript(text);
		} else {
			var script = document.createElement('script');
			script.setAttribute('type', 'text/javascript');
			script.text = text;
			document.head.appendChild(script);
			document.head.removeChild(script);
		}
		return text;
	};
	
	String.implement('stripScripts', function(exec){
		var scripts = '';
		var text = this.replace(/<script[^>]*>([\s\S]*?)<\/script>/gi, function(all, code){
			scripts += code + '\n';
			return '';
		});
		if (exec === true) Browser.exec(scripts);
		else if (typeOf(exec) == 'function') exec(scripts, text);
		return text;
	});
	
	// Window, Document
	
	Browser.extend({
		Document: this.Document,
		Window: this.Window,
		Element: this.Element,
		Event: this.Event
	});
	
	this.Window = this.$constructor = new Type('Window', function(){});
	
	this.$family = Function.from('window').hide();
	
	Window.mirror(function(name, method){
		window[name] = method;
	});
	
	this.Document = document.$constructor = new Type('Document', function(){});
	
	document.$family = Function.from('document').hide();
	
	Document.mirror(function(name, method){
		document[name] = method;
	});
	
	document.html = document.documentElement;
	if (!document.head) document.head = document.getElementsByTagName('head')[0];
	
	if (document.execCommand) try {
		document.execCommand("BackgroundImageCache", false, true);
	} catch (e){}
	
	/*<ltIE9>*/
	if (this.attachEvent && !this.addEventListener){
		var unloadEvent = function(){
			this.detachEvent('onunload', unloadEvent);
			document.head = document.html = document.window = null;
		};
		this.attachEvent('onunload', unloadEvent);
	}
	
	// IE fails on collections and <select>.options (refers to <select>)
	var arrayFrom = Array.from;
	try {
		arrayFrom(document.html.childNodes);
	} catch(e){
		Array.from = function(item){
			if (typeof item != 'string' && Type.isEnumerable(item) && typeOf(item) != 'array'){
				var i = item.length, array = new Array(i);
				while (i--) array[i] = item[i];
				return array;
			}
			return arrayFrom(item);
		};
	
		var prototype = Array.prototype,
			slice = prototype.slice;
		['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift', 'concat', 'join', 'slice'].each(function(name){
			var method = prototype[name];
			Array[name] = function(item){
				return method.apply(Array.from(item), slice.call(arguments, 1));
			};
		});
	}
	/*</ltIE9>*/

});